package Server.players;

import java.util.Hashtable;
import java.util.Vector;

import common.exception.ExtinctionException;

/**
 * Rappresenta un contenitore dei giocatori, che è possibile interrogare per le funzioni di gioco
 * @author Flavio Ferrara
 *
 */
public class PlayerList{

	/**
	 * Deve tenere traccia di:
	 * - utenti registrati
	 * - credenziali
	 * - utenti connessi
	 * - utenti in gioco
	 * - utente corrente
	 * 
	 */
	public static final int MAX_PLAYERS = 8;
	private Hashtable<String, Player> registeredPlayers;
	
	/* l'indice di questa lista rappresenta il token */
	private Vector<String> loggedPlayers;
	
	private Vector<String> inGamePlayers;
	private Vector<String> extinctPlayers;
	
	/* Rappresenta l'indice(+1) della lista inGamePlayers */
	private int currentPlayer = 1;
	
	public PlayerList() {
		this.registeredPlayers = new Hashtable<String, Player>();
		this.loggedPlayers = new Vector<String>();
		this.inGamePlayers = new Vector<String>();
		this.extinctPlayers = new Vector<String>();
	}
	
	/*
	 * Observer
	 */
	
	public boolean isInGame(Player p){
		if (this.inGamePlayers.contains(p.getName()))
			return true;
		return false;
	}
	
	public Player get(int token){
		try{
			return this.registeredPlayers.get(this.loggedPlayers.get(token-1));
		}
		catch (ArrayIndexOutOfBoundsException e){
			return null;
		}
	}
	
	public String[] getInGamePlayers(){
		try{
			return this.inGamePlayers.toArray(new String[this.inGamePlayers.size()]);
		} catch (ArrayStoreException e){
			return new String[0];
		}
	}
	
	public Player getCurrentPlayer(){
		try{
			return new Player(this.registeredPlayers.get(this.inGamePlayers.get(this.currentPlayer - 1)));
		} catch (ArrayIndexOutOfBoundsException e){
				return null;
			}
	}
	
	public boolean existsSpecies(String name) {
		for (Player p : this.registeredPlayers.values()){
			if (p.getSpeciesName().equals(name))
				return true;
		}
		return false;
	}
	public boolean exists(String username) {
		for (Player p : this.registeredPlayers.values()){
			if (p.getName().equals(username))
				return true;
		}
		return false;
	}
	public boolean existsDino(Dino dino){
		for (Player p : this.registeredPlayers.values()){
			if (p.owns(dino))
				return true;
		}
		return false;
	}
	
	/**
	 * Aggiorna il giocatore corrente e ritorna il nuovo corrente
	 * 
	 * @return
	 */
	public Player nextCurrentPlayer() {
		int nextCurrent;
		if (this.currentPlayer >= this.inGamePlayers.size()){
			nextCurrent = 1;
		} else
			nextCurrent = this.currentPlayer + 1;
		
		try{
			Player current = this.registeredPlayers.get(this.inGamePlayers.get(nextCurrent - 1));
			this.currentPlayer = nextCurrent;
			return current;
		} catch (ArrayIndexOutOfBoundsException e){
			return null;
		}
	}
	
	/*
	 * Modificatori
	 */
	
	
	public void add(Player p){
		this.registeredPlayers.put(p.getName(), new Player(p));
	}
	
	/**
	 * @return -1 se è fallito, il token se è andato bene
	 */
	public int login(String username, String passwd){
		try{
			if (this.loggedPlayers.contains(username))
				return this.loggedPlayers.indexOf(username) + 1;
			
			Player p = this.registeredPlayers.get(username);
			if (p.authenticate(passwd)){
				this.loggedPlayers.add(username);
				return this.loggedPlayers.indexOf(username) + 1;
			}
		} catch (RuntimeException e){
			return -1;
		}
		return -1;
	}
	
	public void logout(Player p){
		if (this.loggedPlayers.contains(p.getName())){
			this.loggedPlayers.remove(p.getName());
			this.loggedPlayers.trimToSize();
		}
	}
	public boolean inGame(Player p){
		if (this.isInGame(p))
			return true;
		
		if (this.loggedPlayers.contains(p.getName()) && ! this.extinctPlayers.contains(p.getName())){
			if (this.inGamePlayers.size() < MAX_PLAYERS){
				this.inGamePlayers.add(p.getName());
				return true;
			}
		}
		return false;
	}
	
	public void outGame(Player p){
		if (this.inGamePlayers.contains(p.getName())){
			this.inGamePlayers.remove(p.getName());
			this.inGamePlayers.trimToSize();
		}
	}
	
	public Dino newSpecies(Player p, String name, char type, int firstDinoID){
		Player player = this.registeredPlayers.get(p.getName());
		player.newSpecies(name, type);
		return player.newDino(firstDinoID);
	}
	/**
	 * Il metodo controlla l'id e restituisce il proprietario del dinosauro dino, se esiste
	 * @param dino
	 * @return il proprietario di dino
	 */
	public Player getOwner(Dino dino) {
		for (Player p : this.registeredPlayers.values()){
			if (p.owns(dino))
					return p;
		}
		return null;
	}

	
	/**
	 * Ritorna il dinosauro con ID specificato, a chiunque appartenga
	 * UNICO METODO CHE RICHIEDE UN ID
	 * @param idDino
	 * @return
	 */
	public Dino getDino(int idDino) {
		for (Player p : this.registeredPlayers.values()){
			Dino dino = p.getDino(idDino);
			if (dino != null)
				return dino;
		}
		return null;
	}
	
	public void updateAges() throws ExtinctionException{
		Player p;
		for (String name : this.inGamePlayers){
			p = this.registeredPlayers.get(name);
			p.oneTurnOlder(false);
		}
		
	}

	public void extinct(Player player) {
		this.outGame(player);
		this.extinctPlayers.add(player.getName());
	}
}
